#include "config.h"

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>

#define DBG_SUBSYS S_LIBCONTROL

#include "limits.h"
#include "adt.h"
#include "sysy_lib.h"
#include "bmap.h"
#include "net_table.h"
#include "configure.h"
#include "table_proto.h"
#include "volume_proto.h"
#include "metadata.h"
#include "md_proto.h"
#include "md_map.h"
#include "net_global.h"
#include "volume_ctl.h"
#include "../storage/stor_rpc.h"
#include "volume.h"
#include "job_dock.h"
#include "ylog.h"
#include "dbg.h"

STATIC void __volume_proto_latency_dump(latency_t *latency, time_t now, char *buf)
{
        uint64_t total_count = 0, total_used = 0;
        time_t now_sec = now / USEC_PER_SEC;

        total_count = latency->read_count + latency->write_count;
        total_used  = latency->read_used + latency->write_used;

        if (now_sec != latency->last_output) {
                snprintf(buf, MAX_INFO_LEN, "read: %llu\n"
                                "read_used: %llu\n"
                                "read_latency: %llu\n"
                                "write: %llu\n"
                                "write_used: %llu\n"
                                "write_latency: %llu\n"
                                "average_latency: %llu\n"
                                "time:%u\n",
                                (LLU)latency->read_count,
                                (LLU)latency->read_used,
                                (LLU)(latency->read_count == 0 ? 0 : latency->read_used/latency->read_count),
                                (LLU)latency->write_count,
                                (LLU)latency->write_used,
                                (LLU)(latency->write_count == 0 ? 0 : latency->write_used/latency->write_count),
                                (LLU)(total_count == 0 ? 0 : total_used/total_count),
                                (int)now_sec);

                latency->read_count = 0;
                latency->read_used = 0;
                latency->write_count = 0;
                latency->write_used = 0;
                latency->last_output = now_sec;
        }
}

int volume_proto_latency_begin(volume_proto_t *volume_proto, time_t *begin, int *record, int op)
{
        time_t now;
        latency_t *latency;

        latency = &volume_proto->latency;

        *record = 0;
        if (latency->valid) {
                if (op == __OP_READ) {
                        now = ytime_gettime();
                        if (now - latency->read_last > latency->valid ||
                                        latency->read_last - now > latency->valid) {
                                *record = 1;
                                *begin = now;
                                latency->read_last = now;
                        }
                } else if (op == __OP_WRITE) {
                        now = ytime_gettime();
                        if (now - latency->write_last > latency->valid ||
                                        latency->write_last - now > latency->valid) {
                                *record = 1;
                                *begin = now;
                                latency->write_last = now;
                        }
                } else {
                        YASSERT(0);
                }
        }

        return 0;
}

int volume_proto_latency_record(volume_proto_t *volume_proto, time_t begin, int record, int op)
{
        int ret;
        char buf[MAX_INFO_LEN], path[MAX_PATH_LEN];
        fileid_t *fileid;
        latency_t *latency;
        time_t now;
        uint64_t used;

        buf[0] = '\0';
        fileid = &volume_proto->chkid;
        latency = &volume_proto->latency;

        if (latency->valid && record) {
                now = ytime_gettime();
                used = now - begin;

                if (op == __OP_READ) {
                        latency->read_count++;
                        latency->read_used += used;
                } else if (op == __OP_WRITE) {
                        latency->write_count++;
                        latency->write_used += used;
                } else {
                        YASSERT(0);
                }

                __volume_proto_latency_dump(latency, now, buf);
        }

        if (strlen(buf)) {
                snprintf(path, MAX_PATH_LEN, "/dev/shm/lich4/volume/latency/%s/"CHKID_FORMAT"",
                                volume_proto->table1.pool, CHKID_ARG(fileid));
                ret = _set_text(path, buf, strlen(buf) + 1, O_CREAT | O_TRUNC);
                if (ret)
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int volume_proto_latency_init(volume_proto_t *volume_proto)
{
        latency_t *latency;

        latency = &volume_proto->latency;
        latency->last_output = 0;

        latency->read_last = 0;
        latency->write_last = 0;

        return 0;
}

void volume_proto_latency_destroy(volume_proto_t *volume_proto)
{
        char path[MAX_PATH_LEN];
        fileid_t *fileid;

        fileid = &volume_proto->chkid;
        snprintf(path, MAX_PATH_LEN, "/dev/shm/lich4/volume/latency/"CHKID_FORMAT"",
                        CHKID_ARG(fileid));

        unlink(path);
}
